#include "boardclient.h"
#include <QTimer>
#include <QTcpSocket>
#include <QMutexLocker>
#include <QDebug>
#include <QThread>
#include "command_parse.h"
#include "command_define.h"
#include <QThread>
#include <cmath>
#include <QtEndian>

unsigned int BoardClient::GPIO0_BASE_ADDR = 0x40000000;

BoardClient::BoardClient(QObject *parent)
    : QObject(parent)
{
    // Initialize the gpio device
    qDebug() << gpio_setup(&handle_gpio0, GPIO0_BASE_ADDR);
    start_timer = new QTimer;

//    camera_open(&handle_gpio0, 0xffffff);
    socket = new QTcpSocket(this);
    initialTime();
    connect(this, &BoardClient::sendData, this, &BoardClient::onWritData);
    connect(start_timer, &QTimer::timeout, this, &BoardClient::onCameraPowerOn);
    start_timer->start(1000);

    //配置光敏
    int slave_id = 72;
    camera_adc_set_read(&handle_gpio0, slave_id);
}

BoardClient::~BoardClient()
{
}

void BoardClient::onCameraPowerOn()
{
    if(num >= 5){
        start_timer->stop();
    }
    num++;
    unsigned int temp = 0;
    temp = pow(16, num)-1;
    camera_open(&handle_gpio0, temp);
}

void BoardClient::initialTime()
{
    timer = new QTimer(this);
    command_timer = new QTimer(this);
    heartbeat_timer = new QTimer(this);
    //检测连接超时的心跳
    connect(timer, &QTimer::timeout, this, &BoardClient::onScanServer);
    //接收指令的心跳
    connect(command_timer, &QTimer::timeout, this, &BoardClient::onCommandParse);
    connect(heartbeat_timer, &QTimer::timeout, this, &BoardClient::onHeartbeat);
    timer->start(1000);
}

void BoardClient::onScanServer()
{
    socket->connectToHost(serverIp, serverPort);
    //等待连接成功//检测连接是否超时
    if(!socket->waitForConnected(nTimeOut)){
        socket->disconnect();
    }else{
        //连接QTcpSocket的信号槽，以读取新数据
        timer->stop();
        command_timer->start(100);
        heartbeat_timer->start(1000);
        qDebug()<<"connect sucess";
        QObject::connect(socket, &QTcpSocket::readyRead, this, &BoardClient::OnReadData);
    }
}

void BoardClient::OnReadData()
{
    QMutexLocker lock(&mutex);
    lastRemainData.append(socket->readAll());
//    qDebug()<<"lastRemainData"<<lastRemainData.toHex();
    QList<QByteArray> list_data;
    CommandParse::ParseCommands(lastRemainData, list_data);
    for(auto &v : list_data){
        listBuffers.append(v);
    }
}

void BoardClient::onWritData(const QByteArray &data)
{
//        qDebug()<<"write"<<data.toHex();
        int res = socket->write(data);
        socket->flush();
        if(res == -1){
            qDebug()<<"write wrong";
            socket->disconnect();
            command_timer->stop();
            heartbeat_timer->stop();
            timer->start();
        }
}

void BoardClient::onCommandParse()
{
    QList<QByteArray> commands = GetListCommands();//获得数据
    QList<QByteArray> temp_commands = commands;
    if(commands.size() != 0){
        //            qDebug()<<"commands"<<commands;
        for(auto &v : commands){
            if(checkRecvCommand(v)){
                //should handl the recv data
                temp_commands.removeOne(v);
            }
        }
        SetListCommands(temp_commands);
    }
}

void BoardClient::onHeartbeat()
{
    QByteArray arr;
    QByteArray a;
    arr = CommandParse::BuildSendData(0x01, device_num_, Command::CmdHeartBeat, a);
    emit sendData(arr);
}

QList<QByteArray> BoardClient::GetListCommands()
{
    QMutexLocker lock(&mutex);
    return listBuffers;
}

//对指令进行解析
bool BoardClient::checkRecvCommand(const QByteArray &cmd)
{
//    qDebug() << "cmd" << cmd.toHex();
        const char* p_data1 = (const char*)cmd.data();
        int frame_num = sizeof (Command::CommandHeader::preamble);
        bool ok;
    //#ifdef BigAndSmall
    //    quint16 frame = qFromBigEndian(cmd.mid(frame_num, 2).toHex().toUShort(&ok, 16));
    //#elseT
        quint16 frame = cmd.mid(frame_num, 2).toHex().toUShort(&ok, 16);
    //#endifheartbeat_timer = new QTimer(this);
    //    qDebug()<<"frame"<<frame;
        int code_num = sizeof (Command::CommandHeader::preamble) + sizeof (Command::CommandHeader::frameNum) + sizeof (Command::CommandHeader::cameraId);
        QByteArray data = cmd.mid(sizeof(Command::CommandHeader), cmd.size() - CommandParse::ReceiveDataMinBytes());
        switch (p_data1[code_num]) {
        case Command::RecvExternExposure:{
            qDebug() << "RecvExternExposure";
            externalTrigger(frame);
            return true;
        }

        case Command::RecvRestart:{
            qDebug() << "RecvRestart";
            restartCamera(frame, data);
            return true;
        }
        case Command::RecvPhotoMetry:{
            qDebug() << "RecvPhotoMetry";
            metryLight(frame);
            return true;
        }
        }
        return false;
}

void BoardClient::SetListCommands(QList<QByteArray> array)
{
    QMutexLocker lock(&mutex);
    listBuffers = array;
}

void BoardClient::restartCamera(quint16 frame, QByteArray data)
{
    bool ok;
    unsigned char index = data.toHex().toUInt(&ok, 16);
    if(index >= 25){
        electricity_ = 0;
        camera_close(&handle_gpio0, electricity_);//相机下电
        QThread::sleep(1);
        electricity_ = 16777215;
        camera_open(&handle_gpio0, electricity_);//相机上电
    }else{
        electricity_ -= pow(2, index-1);
        qDebug() << electricity_ << index << "poewr";
        camera_close(&handle_gpio0, electricity_);//相机下电
        QThread::sleep(1);
        electricity_ += pow(2, index-1);
        camera_open(&handle_gpio0, electricity_);//相机上电
    }
    QByteArray arr;
    QByteArray a;
    a = a.append(index);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdRestart, a);
    emit sendData(arr);
}

void BoardClient::externalTrigger(quint16 frame)
{
//    trigger_timer_ = new QTimer(this);
//    connect(trigger_timer_, &QTimer::timeout, this, [&](){
        camera_exp_2us(&handle_gpio0);//触发一次2us脉冲
//        qDebug() << "trigger";
//    });
//    trigger_timer_->start(1000);
    QByteArray arr;
    QByteArray a;
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdExternExposure, a);
    emit sendData(arr);
}

void BoardClient::metryLight(quint16 frame)
{
    QByteArray arr;
    QByteArray a;
    unsigned short temp;//光的强度
    int num = adc_value(&handle_gpio0);
    temp = num & 0xffff;
#ifdef BigAndSmall
    temp = qFromBigEndian(temp);
#endif
    a.append(CommandParse::ShortToArray(temp));
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdPhotoMetry, a);
    emit sendData(arr);
}

